package client

import (
	"bufio"
	"fmt"
	"net"
	"os"
	"strings"
	"sync"
)

type Client struct {
	ServerIp   string
	ServerPort int
	conn       net.Conn
	Name       string
	Password   string
	state      State
	toUser     string
	toGroup    string
}

// New 创建客户端
func New(serverIp string, serverPort int) *Client {
	return &Client{
		ServerIp:   serverIp,
		ServerPort: serverPort,
		state:      UnLogin,
	}
}

// Start 启动客户端
func (client *Client) Start() {
	//连接server
	conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", client.ServerIp, client.ServerPort))
	if err != nil {
		logError("net.Dial error:", err)
		return
	}
	client.conn = conn

	//标记程序退出
	wg := sync.WaitGroup{}
	wg.Add(1)

	//后台goroutine接收server消息
	go client.dealServerMsg(&wg)

	//后台goroutine处理client业务
	go client.doBusiness(&wg)

	//等待程序退出通知
	waitForExit(&wg)
}

func waitForExit(wg *sync.WaitGroup) {
	wg.Wait()
	showMsg("客户端退出")
}

func (client *Client) doBusiness(wg *sync.WaitGroup) {
	//读取stdin，直到按下Ctrl-Z
	scanner := bufio.NewScanner(os.Stdin)
	client.printShellPrompt()
	for scanner.Scan() {
		line := scanner.Text()
		client.parseInput(line)
		client.printShellPrompt()
	}
	//当按下Ctrl-Z后，通知退出
	wg.Done()
}

func (client *Client) parseInput(input string) {
	if len(input) == 0 {
		return
	}

	if strings.HasPrefix(input, "\\") {
		//是cmd
		tokens := strings.Split(input, " ")
		if len(tokens) == 0 {
			return
		}
		cmd := tokens[0]
		switch cmd {
		case HelpCmd:
			client.onHelpCmd()
		case ExitCmd:
			client.onExitCmd()
		case SignInCmd:
			client.onSignInCmd(input)
		case SignUpCmd:
			client.onSignUpCmd(input)
		case AddFriendCmd:
			client.onAddFriendCmd(input)
		case DelFriendCmd:
			client.onDelFriendCmd(input)
		case GetFriendStatusCmd:
			client.onGetFriendStatusCmd()
		case ToUserCmd:
			client.onToUserCmd(input)
		case CreateGroupCmd:
			client.onCreateGroupCmd(input)
		case JoinGroupCmd:
			client.onJoinGroupCmd(input)
		case ExitGroupCmd:
			client.onExitGroupCmd(input)
		case ToGroupCmd:
			client.onToGroupCmd(input)
		case RemoveAccountCmd:
			client.onRemoveAccount()
		}
	} else {
		//是聊天内容
		switch client.state {
		case UserChat:
			client.onUserMsg(input)
		case GroupChat:
			client.onGroupMsg(input)
		}
	}
}

//帮助操作
func (client *Client) onHelpCmd() {
	txt := `客户端帮助信息
\help 显示帮助信息
\exit 退出
\signin <user> <password> 注册
\signup <user> <password> 登录
\addfriend <user> 添加好友
\delfriend <user> 删除好友
\getfriendstatus 查询好友在线情况
\touser 进入私聊模式
\creategroup <group> 建群
\joingroup <group> 加群
\exitgroup <group> 退群
\togroup <group> 进入群聊模式
\removeaccount 删除删号`
	showMsg(txt)
}

//退出操作
func (client *Client) onExitCmd() {
	client.conn.Close()
}

//注册操作
func (client *Client) onSignInCmd(input string) {
	tokens := strings.Split(input, " ")
	if len(tokens) != 3 {
		showMsg("[注册格式错误]")
		return
	}
	user := tokens[1]
	passwd := tokens[2]

	msg := fmt.Sprintf("signin|%s|%s", user, passwd)
	client.sendMsg(msg)
}

//登录操作
func (client *Client) onSignUpCmd(input string) {
	tokens := strings.Split(input, " ")
	if len(tokens) != 3 {
		showMsg("[登录格式错误]")
		return
	}
	user := tokens[1]
	passwd := tokens[2]

	msg := fmt.Sprintf("signup|%s|%s", user, passwd)
	client.sendMsg(msg)
}

//添加好友操作
func (client *Client) onAddFriendCmd(input string) {
	if client.state == UnLogin {
		showMsg("[请先登录]")
		return
	}

	tokens := strings.Split(input, " ")
	if len(tokens) != 2 {
		showMsg("[添加好友格式错误]")
		return
	}
	user := tokens[1]

	msg := fmt.Sprintf("addfriend|%s", user)
	client.sendMsg(msg)
}

func (client *Client) onDelFriendCmd(input string) {
	if client.state == UnLogin {
		showMsg("[请先登录]")
		return
	}

	tokens := strings.Split(input, " ")
	if len(tokens) != 2 {
		showMsg("[添加好友格式错误]")
		return
	}
	user := tokens[1]

	msg := fmt.Sprintf("delfriend|%s", user)
	client.sendMsg(msg)
}

func (client *Client) onGetFriendStatusCmd() {
	if client.state == UnLogin {
		showMsg("[请先登录]")
		return
	}

	msg := "getfriendstatus"
	client.sendMsg(msg)
}

func (client *Client) onToUserCmd(input string) {
	if client.state == UnLogin {
		showMsg("[请先登录]")
		return
	}

	tokens := strings.Split(input, " ")
	if len(tokens) != 2 {
		showMsg("[私聊格式错误]")
		return
	}
	user := tokens[1]

	client.state = UserChat
	client.toUser = user
}

func (client *Client) onCreateGroupCmd(input string) {
	if client.state == UnLogin {
		showMsg("[请先登录]")
		return
	}

	tokens := strings.Split(input, " ")
	if len(tokens) != 2 {
		showMsg("[创建群聊格式错误]")
		return
	}

	group := tokens[1]

	msg := fmt.Sprintf("creategroup|%s", group)
	client.sendMsg(msg)
}

func (client *Client) onJoinGroupCmd(input string) {
	if client.state == UnLogin {
		showMsg("[请先登录]")
		return
	}

	tokens := strings.Split(input, " ")
	if len(tokens) != 2 {
		showMsg("[加入群聊格式错误]")
		return
	}

	group := tokens[1]

	msg := fmt.Sprintf("joingroup|%s", group)
	client.sendMsg(msg)
}

func (client *Client) onExitGroupCmd(input string) {
	if client.state == UnLogin {
		showMsg("[请先登录]")
		return
	}

	tokens := strings.Split(input, " ")
	if len(tokens) != 2 {
		showMsg("[退出群聊格式错误]")
		return
	}

	group := tokens[1]

	msg := fmt.Sprintf("exitgroup|%s", group)
	client.sendMsg(msg)
}

func (client *Client) onToGroupCmd(input string) {
	if client.state == UnLogin {
		showMsg("[请先登录]")
		return
	}

	tokens := strings.Split(input, " ")
	if len(tokens) != 2 {
		showMsg("[群聊格式错误]")
		return
	}
	group := tokens[1]

	client.state = GroupChat
	client.toGroup = group
}

func (client *Client) onRemoveAccount() {
	if client.state == UnLogin {
		showMsg("[请先登录]")
		return
	}

	msg := "removeaccount"
	client.sendMsg(msg)
}

func (client *Client) onUserMsg(input string) {
	if len(input) == 0 {
		showMsg("[请输入聊天内容]")
		return
	}
	if client.state == UnLogin {
		showMsg("[请先登录]")
		return
	}

	msg := fmt.Sprintf("usermsg|%s|%s", client.toUser, input)
	client.sendMsg(msg)
}

func (client *Client) onGroupMsg(input string) {
	if len(input) == 0 {
		showMsg("[请输入聊天内容]")
		return
	}
	if client.state == UnLogin {
		showMsg("[请先登录]")
		return
	}

	msg := fmt.Sprintf("groupmsg|%s|%s", client.toGroup, input)
	client.sendMsg(msg)
}

func (client *Client) sendMsg(msg string) {
	//末尾加换行符是为了服务端Scanner.Scan识别
	client.conn.Write([]byte(msg + "\n"))
}

func (client *Client) printShellPrompt() {
	fmt.Printf("[%s]> ", client.getStateTxt())
}

func (client *Client) getStateTxt() string {
	switch client.state {
	case UnLogin:
		return "离线"
	case Login:
		return "已登录"
	case UserChat:
		return fmt.Sprintf("好友 %s", client.toUser)
	case GroupChat:
		return fmt.Sprintf("群聊 %s", client.toGroup)
	default:
		return "Unknown"
	}
}

func (client *Client) dealServerMsg(wg *sync.WaitGroup) {
	//读取TCP数据，直到TCP读连接关闭
	scanner := bufio.NewScanner(client.conn)
	for scanner.Scan() {
		msg := scanner.Text()
		client.parseServerMsg(msg)
		//showMsg("[Server]", msg)
		client.printShellPrompt()
	}
	//与server断开连接后，通知退出
	showMsg("与server断开连接")
	wg.Done()
}

func (client *Client) parseServerMsg(msg string) {
	if msg == "signinok" {
		showMsg("[Server]", "注册成功")
		return
	}

	if strings.HasPrefix(msg, "signinfail") {
		showMsg("[Server]", "注册失败:"+strings.Split(msg, "|")[1])
		return
	}

	if strings.HasPrefix(msg, "signupok") {
		showMsg("[Server]", "登录成功")
		client.Name = strings.Split(msg, "|")[1]
		client.state = Login
		return
	}

	if strings.HasPrefix(msg, "signupfail") {
		showMsg("[Server]", "登录失败:"+strings.Split(msg, "|")[1])
		return
	}

	if msg == "addfriendok" {
		showMsg("[Server]", "添加好友成功")
		return
	}

	if strings.HasPrefix(msg, "addfriendfail") {
		showMsg("[Server]", "添加好友失败:"+strings.Split(msg, "|")[1])
		return
	}

	if msg == "delfriendok" {
		showMsg("[Server]", "删除好友成功")
		return
	}

	if strings.HasPrefix(msg, "delfriendfail") {
		showMsg("[Server]", "删除好友失败:"+strings.Split(msg, "|")[1])
		return
	}

	if strings.HasPrefix(msg, "getfriendstatusfail") {
		showMsg("[Server]", "查询好友在线失败:"+strings.Split(msg, "|")[1])
		return
	}

	if strings.HasPrefix(msg, "friendstatus") {
		tokens := strings.Split(msg, "|")
		if len(tokens) != 3 {
			return
		}
		onlineFriends := strings.Split(tokens[1], ",")
		offlineFriends := strings.Split(tokens[2], ",")
		txt := strings.Builder{}
		txt.WriteString("好友在线情况：\n")
		txt.WriteString("在线：\n")
		txt.WriteString(strings.Join(onlineFriends, "\n") + "\n")
		txt.WriteString("离线：\n")
		txt.WriteString(strings.Join(offlineFriends, "\n") + "\n")

		showMsg("[Server]", txt.String())
		return
	}

	if strings.HasPrefix(msg, "notifyfriendonline") {
		tokens := strings.Split(msg, "|")
		if len(tokens) != 2 {
			return
		}
		friend := tokens[1]
		showMsg(fmt.Sprintf("[好友上线] %s", friend))
		return
	}

	if strings.HasPrefix(msg, "notifyfriendoffline") {
		tokens := strings.Split(msg, "|")
		if len(tokens) != 2 {
			return
		}
		friend := tokens[1]
		showMsg(fmt.Sprintf("[好友下线] %s", friend))
		return
	}

	if strings.HasPrefix(msg, "recvusermsg") {
		tokens := strings.Split(msg, "|")
		if len(tokens) != 3 {
			return
		}
		from := tokens[1]
		content := tokens[2]
		showMsg(fmt.Sprintf("[私聊消息(%s)]", from), content)
		return
	}

	if strings.HasPrefix(msg, "usermsgfail") {
		showMsg("[Server]", "发送消息失败:"+strings.Split(msg, "|")[1])
		return
	}

	if msg == "creategroupok" {
		showMsg("[Server]", "创建群聊成功")
		return
	}

	if strings.HasPrefix(msg, "creategroupfail") {
		showMsg("[Server]", "创建群聊失败:"+strings.Split(msg, "|")[1])
		return
	}

	if msg == "joingroupok" {
		showMsg("[Server]", "加入群聊成功")
		return
	}

	if strings.HasPrefix(msg, "joingroupfail") {
		showMsg("[Server]", "加入群聊失败:"+strings.Split(msg, "|")[1])
		return
	}

	if msg == "exitgroupok" {
		showMsg("[Server]", "退出群聊成功")
		return
	}

	if strings.HasPrefix(msg, "exitgroupfail") {
		showMsg("[Server]", "退出群聊失败:"+strings.Split(msg, "|")[1])
		return
	}

	if strings.HasPrefix(msg, "recvgroupmsg") {
		tokens := strings.Split(msg, "|")
		if len(tokens) != 4 {
			return
		}
		group := tokens[1]
		from := tokens[2]
		content := tokens[3]
		showMsg(fmt.Sprintf("[群聊消息(%s)(%s)]", group, from), content)
		return
	}

	if strings.HasPrefix(msg, "groupmsgfail") {
		showMsg("[Server]", "发送群聊失败:"+strings.Split(msg, "|")[1])
		return
	}

	if msg == "removeaccountok" {
		showMsg("[Server]", "删除账号成功")
		client.state = UnLogin
		return
	}

	if strings.HasPrefix(msg, "removeaccountfail") {
		showMsg("[Server]", "删除账号失败:"+strings.Split(msg, "|")[1])
		return
	}
}

//--------------------------------------------------------------------

// State 表示客户端处于何种状态
type State uint

const (
	UnLogin   State = iota //未登录
	Login                  //已登录
	UserChat               //私聊
	GroupChat              //群聊
)

// 输入命令的类别
const (
	HelpCmd            string = `\help`            //帮助
	ExitCmd            string = `\exit`            //退出
	SignInCmd          string = `\signin`          //注册
	SignUpCmd          string = `\signup`          //登录
	AddFriendCmd       string = `\addfriend`       //添加好友
	DelFriendCmd       string = `\delfriend`       //删除好友
	GetFriendStatusCmd string = `\getfriendstatus` //查询好友在线情况
	ToUserCmd          string = `\touser`          //进入与好友私聊模式
	CreateGroupCmd     string = `\creategroup`     //建群
	JoinGroupCmd       string = `\joingroup`       //加群
	ExitGroupCmd       string = `\exitgroup`       //退群
	ToGroupCmd         string = `\togroup`         //进入群聊模式
	RemoveAccountCmd   string = `\removeaccount`   //删除账号
)

// 打印信息
func showMsg(msg ...interface{}) {
	fmt.Println(msg...)
}

// 打印错误信息
func logError(errMsg ...interface{}) {
	fmt.Println(errMsg...)
}
